#include "config.h"

#include <sys/file.h>
#include <sys/types.h>
#include <regex.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <sys/vfs.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/statvfs.h>
#include <dirent.h>

#define DBG_SUBSYS S_LIBINTERFACE

#include "sysy_lib.h"
#include "ynet_rpc.h"
#include "env.h"
#include "lichstor.h"
#include "metadata.h"
#include "storage.h"
#include "get_version.h"
#include "configure.h"
#include "core.h"
#include "fence.h"
#include "lich_nfs.h"
#include "lich_nbd.h"
#include "lich_sheepdog.h"
#include "lichconf.h"
#include "lichstor.h"
#include "job_dock.h"
#include "minirpc.h"
#include "rpc_proto.h"
#include "cluster.h"
#include "lichbd.h"
#include "lichbd_rpc.h"
#include "chunk.h"
#include "iscsid.h"
#include "prof.h"
#include "main_loop.h"
#include "net_global.h"
#include "net_vip.h"
#include "jnl_proto.h"
#include "analysis.h"
#include "ylog.h"
#include "dbg.h"
#include "bh.h"
#include "disk.h"
#include "volume_ctl.h"

//static int tmpmode = 0;

static sem_t exit_sem;

static void usage()
{
        printf("    lichgw -f\n");
        return;
}

static  void gateway_monitor_handler(int sig)
{
        DINFO("monitor got signal %d, dump...\n", sig);
        //exit(0);
}

static void gateway_exit_handler(int sig)
{
        DINFO("got signal %d, prepare exit, please waiting\n", sig);

        srv_running = 0;

        netvip_destroy();

        //volume_ctl_destroy_all();

        sem_post(&exit_sem);
}

static void signal_handler(int sig)
{
        (void) sig;

        DINFO("got signal %d, load %llu\n", sig, (LLU)jobdock_load());

        netable_iterate();
        analysis_dumpall();
}

static int __gateway_run(void *_home)
{
        int i, ret, lockfd, step = 0;
        char path[MAX_PATH_LEN];
        const char *home = _home;

        signal(SIGUSR1, signal_handler);
        signal(SIGUSR2, gateway_exit_handler);
        signal(SIGTERM, gateway_exit_handler);
        signal(SIGHUP, gateway_exit_handler);
        signal(SIGKILL, gateway_exit_handler);
        signal(SIGINT, gateway_exit_handler);

        ret = env_init_common(0);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        strcpy(ng.home, home);
        
        for(i = 0; i < YLOG_TYPE_MAX; i++){
                ylog_reset(i);
                ylog_set_parent(i, 0);
        }

        snprintf(path, MAX_NAME_LEN, "%s/status/status.pid", home);
        ret = daemon_pid(path);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        lockfd = daemon_lock(path);
        if (lockfd < 0) {
                ret = -lockfd;
                GOTO(err_ret, ret);
        }

        ret = lichbd_init_new();
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = core_init(0);
        if (unlikely(ret))
                GOTO(err_ret, ret);
        
        if (gloconf.iscsid) {
                ret = iscsid_srv(ISCSID_DRIVER_TCP | ISCSID_DRIVER_ISER);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        }

        ret = env_update_status("running", -1);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        while (srv_running) {
                if (step % 30  == 0) {
                        fence_test();
                }

                step ++;

                ret = _sem_timedwait1(&exit_sem, 2);
                if (unlikely(ret)) {
                        if (ret == ETIMEDOUT)
                                continue;
                        else
                                GOTO(err_ret, ret);
                }
        }

        ret = env_update_status("stopping", -1);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        //stor_destroy();

        ret = env_update_status("stopped", -1);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        DINFO("service exit\n");
        return 0;
err_ret:
        DERROR("service exit with error %d\n", ret);
        return ret;
}

#define SYSYTEM_MAXOPENFILE 1048576


static int __env_prep(int daemon)
{
        int ret, i;
        char path[YLOG_TYPE_MAX][MAX_PATH_LEN];
        int fds[YLOG_TYPE_MAX];
        
        dbg_sub_init();

        if (daemon) {
                snprintf(path[YLOG_TYPE_STD], MAX_PATH_LEN, "%s/log/gateway.log", gloconf.home);
                snprintf(path[YLOG_TYPE_PERF], MAX_PATH_LEN, "%s/log/gateway_perf.log", gloconf.home);
                snprintf(path[YLOG_TYPE_BALANCE], MAX_PATH_LEN, "%s/log/gateway_balance.log", gloconf.home);
                snprintf(path[YLOG_TYPE_RAMDISK], MAX_PATH_LEN, "%s/log/gateway_ramdisk.log", gloconf.home);

                ret = path_validate(path[YLOG_TYPE_STD], 0, 1);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                ret = path_validate(path[YLOG_TYPE_PERF], 0, 1);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                ret = path_validate(path[YLOG_TYPE_BALANCE], 0, 1);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                ret = path_validate(path[YLOG_TYPE_RAMDISK], 0, 1);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                (void) ylog_init(YLOG_FILE, path);

                for(i = 0; i < YLOG_TYPE_MAX; i++){
                        fds[i] = (*g_ylogs)[i].logfd;
                }

                ret =  daemonlize(1, gloconf.maxcore, NULL, fds, YLOG_TYPE_MAX);
                if (unlikely(ret)){
                        GOTO(err_ret, ret);
                }
                
                syslog_init();
        } else {
                (void) ylog_init(YLOG_STDERR, NULL);

                ret = daemonlize(0, gloconf.maxcore, NULL, NULL, 0);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                syslog_init();
        }

        return 0;
err_ret:
        return ret;
}

int main(int argc, char *argv[])
{
        int ret, daemon = 1, c_opt, nomonitor = 0;
        char *home = "/tmp/lichgw", *clustername = NULL;

        while (srv_running) {
                int option_index = 0;

                static struct option long_options[] = {
                        { "home", required_argument, 0, },
                        { "foreground", no_argument, 0, 'f' },
                        { "help", no_argument, 0, 'h' },
                        { 0, 0, 0, 0 },
                };

                c_opt = getopt_long(argc, argv, "fvh", long_options, &option_index);
                if (c_opt == -1)
                        break;

                switch (c_opt) {
                case 0:
                        switch (option_index) {
                        case 0:
                                DBUG("home %s\n", optarg);
                                home = optarg;
                                break;
                        default:
                                fprintf(stderr, "Hoops, wrong op got!\n");
                        }

                        break;
                case 'f':
                        daemon = 0;
                        break;
                case 'v':
                        get_version();
                        goto out;
                case 'h':
                        usage();
                        goto out;
                default:
                        fprintf(stderr, "Hoops, wrong op (%c) got!\n", c_opt);
                        EXIT(1);
                }
        }

        if (home == NULL) {
                usage();
                goto out;
        }

        if (clustername) {
                usage();
                goto out;
        }

        signal(SIGUSR1, gateway_monitor_handler);
        signal(SIGUSR2, gateway_monitor_handler);
        signal(SIGTERM, gateway_monitor_handler);
        signal(SIGHUP, gateway_monitor_handler);
        signal(SIGKILL, gateway_monitor_handler);
        signal(SIGINT, gateway_monitor_handler);

        ret = path_validate(home, YLIB_ISDIR, YLIB_DIRCREATE);
        if (ret)
                GOTO(err_ret, ret);

        ret = conf_init();
        if (unlikely(ret))
                GOTO(err_ret, ret);
        
        if (!sanconf.iscsi_gateway) {
                DBUG("iscsi gateway disabled\n");
                return 0;
        }

        ret = __env_prep(daemon);
        if (ret)
                GOTO(err_ret, ret);
        
        if (nomonitor == 0) {
                char path[MAX_PATH_LEN];
                snprintf(path, MAX_NAME_LEN, "%s/status/parent.pid", home);
                ret = daemon_pid(path);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                ret = env_server_run(daemon, __gateway_run, home);
                if (unlikely(ret)) {
                        GOTO(err_ret, ret);
                }
        } else {
                ret = __gateway_run(home);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        }

out:
        return 0;
err_ret:
        EXIT(ret);
}
